package com.example.demo.async;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Component
@Aspect
public class CacheableAsyncAspect {
  @Autowired
  private RedisTemplate<String, Object> redisTemplate;

  @Pointcut("@annotation(com.example.demo.async.CacheAsync)")
  public void annotationPointcut() {}

  @Around("annotationPointcut()")
  public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
    // 获得当前访问的class
    Class<?> className = joinPoint.getTarget().getClass();
    // 获得访问的方法名
    String methodName = joinPoint.getSignature().getName();
    // 得到方法的参数的类型
    Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
    Object[] args = joinPoint.getArgs();
    String key = "";
    int expireTime = 1800;
    try {
      // 得到访问的方法对象
      Method method = className.getMethod(methodName, argClass);
      method.setAccessible(true);
      Object obj = method.invoke(className.newInstance());
      System.err.println(JSON.toJSONString(obj));
      // 判断是否存在注解
      if (method.isAnnotationPresent(CacheAsync.class)) {
        CacheAsync annotation = method.getAnnotation(CacheAsync.class);
        expireTime = getExpireTime(annotation);
      }
    } catch (Exception e) {
      throw new RuntimeException("redis缓存注解参数异常", e);
    }

    //放入队列
//    redisTemplate.boundListOps("yubei").leftPush()
    // 获取缓存是否存在
//    boolean hasKey = redisTemplate.hasKey(key);
//    if (hasKey) {
//      final String keyf = key.toString();
//      Object object = null;
//      object =
//          redisTemplate.execute(
//              new RedisCallback<Object>() {
//                @Override
//                public Object doInRedis(RedisConnection connection) throws DataAccessException {
//                  byte[] key = keyf.getBytes();
//                  byte[] value = connection.get(key);
//                  if (value == null) {
//                    return null;
//                  }
//                  return toObject(value);
//                }
//              });
//      return object;
//    } else {
//      // 执行原方法（java反射执行method获取结果）
//      Object res = joinPoint.proceed();
//      // 设置缓存
//      String finalKey = key;
//      int finalExpireTime = expireTime;
//      redisTemplate.execute(
//          new RedisCallback<Long>() {
//            @Override
//            public Long doInRedis(RedisConnection connection) throws DataAccessException {
//              byte[] keyb = finalKey.getBytes();
//              byte[] valueb = toByteArray(res);
//              connection.set(keyb, valueb);
//              if (finalExpireTime > 0) {
//                connection.expire(keyb, finalExpireTime);
//              }
//              return 1L;
//            }
//          });
//      return res;
//    }
    return joinPoint.proceed();
  }

  private Object toObject(byte[] bytes) {
    Object obj = null;
    try {
      ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
      ObjectInputStream ois = new ObjectInputStream(bis);
      obj = ois.readObject();
      ois.close();
      bis.close();
    } catch (IOException ex) {
      ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace();
    }
    return obj;
  }

  private byte[] toByteArray(Object obj) {
    byte[] bytes = null;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try {
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      oos.flush();
      bytes = bos.toByteArray();
      oos.close();
      bos.close();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return bytes;
  }

  private int getExpireTime(CacheAsync annotation) {
    return annotation.expireTime();
  }

}
